package UMC.Web;

import UMC.Data.Utility;
import UMC.Security.AccessToken;
import UMC.Security.Identity;

import java.io.IOException;
import java.net.URI;
import java.util.*;
import java.util.stream.Collectors;

public class WebServlet {

    public static void scanning() {
        Package[] packages = Package.getPackages();
        for (Package p : packages) {
            if (p.isAnnotationPresent(Apiumc.class)) {
                PackageParse.scanRoot(p.getName());
            }
        }

    }

    public static WebMeta getAppConf() {
        return WebHost._AppConf;
    }

    public static List<WebMeta> mapping() {
        List<WebMeta> metas = new LinkedList<>();
        if (WebRuntime.facClas.size() > 0) {
            for (Class t : WebRuntime.facClas) {
                WebMeta meta = new WebMeta();
                meta.put("type", t.getName());

                meta.put("name", "." + t.getSimpleName());
                metas.add(meta);

                Apiumc mapping = (Apiumc) t.getAnnotation(Apiumc.class);
                if (!Utility.isEmpty(mapping.desc())) {
                    meta.put("desc", mapping.desc());

                }

            }

        }
        if (WebRuntime.flows.size() > 0) {
            for (Map.Entry<String, List<Class<WebFlow>>> entry : WebRuntime.flows.entrySet()) {

                for (Class<WebFlow> t : entry.getValue()) {
                    WebMeta meta = new WebMeta();
                    meta.put("type", t.getName());
                    metas.add(meta);
                    meta.put("auth", WebRuntime.authKeys.get(entry.getKey()).name());
                    meta.put("model", entry.getKey());

                    meta.put("name", entry.getKey() + ".");
                    Apiumc mapping = (Apiumc) t.getAnnotation(Apiumc.class);
                    if (!Utility.isEmpty(mapping.desc())) {

                        meta.put("desc", mapping.desc());
                    }

                }

            }
        }

        if (WebRuntime.activities.size() > 0) {
            for (Map.Entry<String, Map<String, Class<WebActivity>>> entry : WebRuntime.activities.entrySet()) {
                Map<String, Class<WebActivity>> map = entry.getValue();
                for (Map.Entry<String, Class<WebActivity>> entry1 : map.entrySet()) {
                    Class<WebActivity> t = entry1.getValue();

                    WebMeta meta = new WebMeta();
                    metas.add(meta);

                    Apiumc mapping = (Apiumc) t.getAnnotation(Apiumc.class);
                    meta.put("auth", mapping.auth().name());
                    meta.put("type", t.getName());
                    meta.put("name", mapping.model() + "." + mapping.cmd());
                    meta.put("cmd", entry1.getKey());
                    meta.put("model", entry.getKey());
                    if (mapping.biometric()) {
                        meta.put("biometric", true);
                    }
                    if (!Utility.isEmpty(mapping.desc())) {
                        meta.put("desc", mapping.desc());
                    }
                }

            }
        }
        return metas;

    }

    public void Process(Map<String, String[]> params, Map<String, String[]> headers, String authSecret,
                        URI requestURI, WebClient.IWebClient webClient) {

        String checkSign = "";
        String UserHostAddress = "127.0.0.1";
        String seesionKey = "";
        String organizes = null, roles = null, id = null, name = null, alias = null, UrlReferrer = null,
                UserAgent = null, host = null, scheme = "http";
        Set<Map.Entry<String, String[]>> hds = headers.entrySet();
        Map<String, String> authing = new LinkedHashMap<>();
        int requesttime = 0;
        for (Map.Entry<String, String[]> k : hds) {
            String k2 = k.getKey();
            String value = String.join(",", k.getValue());
            if (k2.startsWith("umc-")) {
                if (k2.endsWith("-sign")) {
                    checkSign = value;
                } else {
                    authing.put(k2, value);
                }
            }
            switch (k2.toLowerCase()) {
                case "umc-redirect-times":
                
                    requesttime = Utility.parse(value, 0);// value;
                    break;
                case "umc-request-user-organizes":
                    organizes = value;
                    break;
                case "umc-request-user-roles":
                    roles = value;
                    break;
                case "umc-request-user-id":
                    id = value;
                    break;
                case "umc-request-user-name":
                    name = value;
                    break;
                case "umc-request-user-alias":
                    alias = value;
                    break;
                case "referer":
                    UrlReferrer = value;
                    break;
                case "user-agent":
                    UserAgent = value;
                    break;
                case "cookie":
                    seesionKey = Arrays.stream(value.split("; ")).filter(d -> d.startsWith("device=")).findFirst()
                            .orElse(seesionKey);
                    if (!Utility.isEmpty(seesionKey)) {
                        seesionKey = seesionKey.substring(7);
                    }
                    break;
                case "content-type":
                    // contentType = value;
                    break;
                case "x-forwarded-host":
                case "x-client-host":
                    host = value;
                    break;
                case "host":
                    if (Utility.isEmpty(host))
                        host = value;
                    break;
                case "x-real-ip":
                case "x-forwarded-for":
                case "x-client-ip":
                    UserHostAddress = value;
                    break;
                case "x-forwarded-proto":
                case "x-client-scheme":
                    scheme = value;
                    break;
            }
        }

        Map<String, String> QueryString = new LinkedHashMap<>();

        String model = null, cmd = null;

        Set<Map.Entry<String, String[]>> set = params.entrySet();
        List<String> values = new LinkedList<>();
        for (Map.Entry<String, String[]> entry : set) {
            String key = entry.getKey();
            switch (key) {
                case "_model":
                    model = String.join(",", entry.getValue());
                    break;
                case "_cmd":
                    cmd = String.join(",", entry.getValue());
                    break;
                case "_":
                    for (String v : entry.getValue()) {
                        if (!Utility.isEmpty(v))
                            values.add(v);
                    }
                    break;
                case "__":
                    for (String v : entry.getValue()) {
                        for (String v2 : v.split("/")) {
                            if (!Utility.isEmpty(v2))
                                values.add(v2);
                        }
                    }
                    break;
                default:

                    if (!key.startsWith("_")) {
                        String value = String.join(",", entry.getValue());
                        QueryString.put(key, value);

                    }
                    break;
            }
        }

        if (QueryString.size() == 1) {
            Map.Entry<String, String> entry = QueryString.entrySet().stream().findFirst().get();
            if (Utility.isEmpty(entry.getKey())) {
                if (!Utility.isEmpty(entry.getValue())) {
                    values.add(entry.getValue());
                }
                QueryString.clear();

            } else if (Utility.isEmpty(entry.getValue())) {
                values.add(entry.getKey());
                QueryString.clear();
            }

        }
        String[] Segments = Arrays.stream(requestURI.getPath().split("/")).filter(r -> !Utility.isEmpty(r))
                .collect(Collectors.toList()).toArray(new String[0]);

        boolean IsForm = false;
        switch (Segments.length) {
            case 1:
            case 0:
                if (Utility.isEmpty(seesionKey)) {
                    seesionKey = Utility.uuid(UUID.randomUUID());
                }
                break;
            case 2:
                seesionKey = Segments[1];
                IsForm = requestURI.getPath().endsWith("/");
                break;
            default:
                model = Segments[1];
                cmd = Segments[2];
                for (int i = 3; i < Segments.length; i++) {
                    values.add(Segments[i]);
                }
                break;
        }

        if (Utility.isEmpty(cmd) || Utility.isEmpty(model)) {
            // webClient.outputWriter().write("command is empty");
            throw new IllegalArgumentException("command is empty");
        }

        AccessToken accessToken = null;
        if (!Utility.isEmpty(authSecret) && !Utility.isEmpty(checkSign) && !Utility.isEmpty(name)) {
            StringBuilder buff = new StringBuilder();
            List<String> stream = authing.keySet().stream().sorted().collect(Collectors.toList());
            for (String k : stream) {
                buff.append(k);
                buff.append("=");
                buff.append(authing.get(k));
                buff.append("&");
            }
            buff.append("key=");
            buff.append(authSecret);
            if (Utility.md5(buff.toString()).equalsIgnoreCase(checkSign)) {
                UUID userId = Utility.uuid(name, true);
                if (!Utility.isEmpty(id)) {
                    userId = Utility.isNull(Utility.uuid(id), userId);
                }
                Identity user = Identity.create(userId, name, alias,
                        Utility.isEmpty(roles) ? new String[0] : roles.split(","),
                        Utility.isEmpty(organizes) ? new String[0] : organizes.split(","));

                accessToken = new AccessToken(Utility.uuid(seesionKey, true)).login(user, 0);

            }
        }
        if (accessToken == null) {
            requesttime = 0;
            UUID uuid = Utility.uuid(seesionKey, true);
            accessToken = new AccessToken(uuid).login(Identity.create(uuid, "?", "Guest"), 0);
        }
        if (Utility.isEmpty(host)) {
            host = requestURI.getAuthority();
        }
        String strQuery = requestURI.getQuery();
        if (Utility.isEmpty(strQuery)) {
            strQuery = "";
        } else {
            strQuery = "?" + strQuery;
        }
        URI _uri = URI.create(String.format("%s://%s%s%s", scheme, host, requestURI.getPath(), strQuery));
        WebClient client = new WebClient(_uri, Utility.isEmpty(UrlReferrer) ? null : URI.create(UrlReferrer), UserAgent,
                UserHostAddress, accessToken, requesttime);

        client.isForm = IsForm;
        if (client.verify(model, cmd)) {
            WebMeta header = new WebMeta();
            if (QueryString.size() > 0) {
                header.put(model, QueryString);
            }
            switch (values.size()) {
                case 0:
                    break;
                case 1:
                    header.put(cmd, values.get(0));
                    break;
                default:
                    header.put(cmd, values.toArray());
                    break;
            }
            client.send(model, cmd, header);

        }

        try {
            client.writeTo(webClient);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
